#pragma once
#include "HPP.hpp"

namespace LOG // namespace LOG
{
    using namespace MyMutex;

    // 默认路径、默认日志文件记录本
    const std::string _path_name = "./"; // 这里需要完善一下路径
    const std::string _txt_name =
        "MyLog.txt"; // 这里放写日志的文件名字
                     // / home / ubuntu / Ubuntu / ubunto - learning
    // 日志等级
    enum class LogLevel
    {
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    // 把日志等级转化成字符串
    std::string LevelToString(LogLevel _out_level)
    {
        switch (_out_level)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARNING:
            return "WARNING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        default:
            return "NONE";
        }
    }

    // 获取时间
    std::string LocalTime()
    {
        // 获取时间
        time_t _time_stamp = ::time(nullptr); // 时间戳
        struct tm _localtime;
        localtime_r(&_time_stamp, &_localtime); // 把时间戳改成可读性更高的方式

        char _buffer[64];
        snprintf(_buffer, sizeof(_buffer), "%4d-%02d-%02d %02d:%02d:%02d",
                 _localtime.tm_year + 1900, _localtime.tm_mon + 1, _localtime.tm_mday,
                 _localtime.tm_hour, _localtime.tm_min,
                 _localtime.tm_sec); // 输出向左补零

        return _buffer;
    }

    // 统一策略方法
    class LogStrategy
    {
    public:
        virtual ~LogStrategy() = default;
        virtual void SyncLog(const std::string &_data) = 0;
    };

    // 控制台策略
    class ConsoleStrategy : public LogStrategy
    {
    public:
        ConsoleStrategy() = default;
        ~ConsoleStrategy() override = default;

        void SyncLog(const std::string &_data) override
        {
            // 打印的时候 需要有一把锁 保护起来
            LockGuard _lock_guard(_lock);
            std::cout << _data << std::endl;
        }

    private:
        Mutex _lock;
    };

    // 这个是文件类
    class FileStrategy : public LogStrategy
    {
    public:
        FileStrategy(const std::string &_out_path_name = _path_name,
                     const std::string &_out_text_name = _txt_name)
            : _pathname(_out_path_name), _logname(_out_text_name)
        {
            // 存路径 要确定一下 路径是否存在
            LockGuard _guard(_lock);

            if (std::filesystem::exists(_pathname)) // 路径存在 返回true
            {
                return;
            }
            try
            {
                std::filesystem::create_directories(_pathname);
            }
            catch (std::filesystem::filesystem_error &_e)
            {
                // 既然错了
                std::cerr << _e.what() << "\n";
            }
        }
        ~FileStrategy() override = default;

        void SyncLog(const std::string &_data) override
        {
            LockGuard _guard(_lock);

            std::string _the_all_log_name = _pathname + "/" + _logname;
            std::ofstream _out(_the_all_log_name, std::ios::app);
            if (!_out.is_open())
            {
                std::cerr << "Failed to open log file: " << _the_all_log_name << "\n";
                return;
            }

            _out << _data << "\n";

            _out.close(); // 文件关掉
        }

    private:
        std::string _pathname; // 将来日志的路径在哪里
        std::string _logname;  // 日志名字

        // 锁 保证资源安全
        Mutex _lock;
    };

    // 写一个日志类
    class Logger
    {
    public:
        Logger()
        {
            // 默认采用在显示器上面打印
            _logstrategy = std::make_shared<ConsoleStrategy>();
        }
        void EnableConsoleStrategy()
        {
            _logstrategy = std::make_shared<ConsoleStrategy>();
        }

        void EnableFileStrategy() { _logstrategy = std::make_shared<FileStrategy>(); }

        ~Logger() = default;

        class LoggerInnerStrategy
        {
            // 表明一条完整的信息 标准部分+可变部分

        public:
            LoggerInnerStrategy(LogLevel _out_level, std::string _out_name,
                                int _out_line, Logger &_out_log)
                : _currenttime(LocalTime()), _level(_out_level), _pid(::getpid()),
                  _file_name(std::move(_out_name)), _line(_out_line),
                  _strategy(_out_log)

            {
                // 拼成一个字符串
                std::stringstream _all;
                _all << "[" << _currenttime << "]"
                     << "[" << LevelToString(_level) << "]"
                     << "["
                     << "pid:" << _pid << "]"
                     << "[" << _file_name << ":" << _line << "] ";

                _loginfo = _all.str(); // 转换
            }

            template <class T>
            LoggerInnerStrategy &operator<<(const T &_out)
            {
                std::stringstream _ss;
                _ss << _out;
                _loginfo += _ss.str();
                return *this;
            }

            ~LoggerInnerStrategy()
            {
                // 为什么放在析构里面？
                if (_strategy._logstrategy)
                {
                    _strategy._logstrategy->SyncLog(_loginfo);
                }
            }

        private:
            std::string _currenttime; // 现在的时间
            LogLevel _level;          // 日志等级
            pid_t _pid;               // 进程PID
            std::string _file_name;   // 文件名字
            uint32_t _line;           // 行数 日志所在的行数

            Logger &_strategy;

            std::string _loginfo; // 一条完整的日志记录
        };

        LoggerInnerStrategy operator()(LogLevel _out_level, std::string _out_name,
                                       int _out_line)
        {
            return LoggerInnerStrategy(_out_level, _out_name, _out_line,
                                       *this);
        }

    private:
        std::shared_ptr<LogStrategy> _logstrategy;
    };

    Logger _logger;

#define LOG(level) _logger(level, __FILE__, __LINE__)

#define ENABLE_CONSOLE() _logger.EnableConsoleStrategy()

#define ENABLE_FILE() _logger.EnableFileStrategy()
} // namespace LOG
